use std::cell::RefCell;
use std::fs::File;
use crate::class_file_packet::ClassFilePacket;

use crate::composite_class_file_packet::CompositeClassFilePacket;
use crate::dir_class_file_packet::DirClassFilePacket;
use crate::jar_class_file_packet::JarClassFilePacket;
use crate::jmod_class_file_packet::JmodClassFilePacket;

pub struct ClassPath {
    pub boot_classpath: CompositeClassFilePacket,
    pub ext_classpath: CompositeClassFilePacket,
    pub user_classpath: CompositeClassFilePacket,
}

impl ClassPath {
    pub fn read_class(&self, class_name: &String) -> Result<Vec<u8>, String> {
        let cf_data = self.boot_classpath.read_class(class_name);
        if let Ok(data) = cf_data {
            return Ok(data);
        }
        let cf_data = self.ext_classpath.read_class(class_name);
        if let Ok(data) = cf_data {
            return Ok(data);
        }
        let cf_data = self.user_classpath.read_class(class_name);
        if let Ok(data) = cf_data {
            return Ok(data);
        }
        return Err(String::from("Class Not Found"));
    }


    pub fn new(jdk_lib_path: &String, class_path: &String) -> Result<ClassPath, String> {
        let result = ClassPath {
            boot_classpath: CompositeClassFilePacket { class_file_packet_list: RefCell::new(Vec::new()) },
            ext_classpath: CompositeClassFilePacket { class_file_packet_list: RefCell::new(Vec::new()) },
            user_classpath: CompositeClassFilePacket { class_file_packet_list: RefCell::new(Vec::new()) },
        };


        let jdk_lib_dir = std::fs::read_dir(jdk_lib_path);
        match jdk_lib_dir {
            Ok(dir) => {
                let mut boot_classpath_class_file_packet_list = result.boot_classpath.class_file_packet_list.borrow_mut();
                for dir_entry_result in dir {
                    let dir_entry = dir_entry_result.unwrap();
                    let file_type = dir_entry.file_type().unwrap();
                    let path = dir_entry.path();
                    if file_type.is_dir() {
                        continue;
                    }
                    let ext = path.extension().unwrap().to_str().unwrap();
                    if ext == "jmod" {
                        let v = JmodClassFilePacket { jmod_file_path: RefCell::new(path.to_str().unwrap().to_string()) };
                        boot_classpath_class_file_packet_list.push(Box::new(v));
                    }
                    if ext == "jar" {
                        let v = JarClassFilePacket { jar_file_path: RefCell::new(path.to_str().unwrap().to_string()) };
                        boot_classpath_class_file_packet_list.push(Box::new(v));
                    }
                }
            }
            Err(err) => {
                println!("{}", err)
            }
        };


        class_path.split(" ").for_each(|spath| {
            let path = std::path::Path::new(spath);
            if path.is_dir() {
                let mut user_classpath_class_file_packet_list = result.user_classpath.class_file_packet_list.borrow_mut();
                let v = DirClassFilePacket { dir_path: RefCell::new(spath.to_string()) };
                user_classpath_class_file_packet_list.push(Box::new(v));
            } else {
                let file_result = File::open(path);
                match file_result {
                    Ok(_file) => {
                        let mut user_classpath_class_file_packet_list = result.user_classpath.class_file_packet_list.borrow_mut();
                        if spath.ends_with(".jmod") {
                            let v = JmodClassFilePacket { jmod_file_path: RefCell::new(spath.to_string()) };
                            user_classpath_class_file_packet_list.push(Box::new(v));
                        }
                        if spath.ends_with(".jar") {
                            let v = JarClassFilePacket { jar_file_path: RefCell::new(spath.to_string()) };
                            user_classpath_class_file_packet_list.push(Box::new(v));
                        }
                    }
                    Err(err) => {
                        println!("path:{}, err:{}", spath, err)
                    }
                };
            }
        });

        return Ok(result);
    }

}
